package cn.gx.kevin.controller;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import cn.gx.kevin.common.annotation.*;
import cn.gx.kevin.common.ui.AjaxResult;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


@Controller
@RequestMapping(value = "/api")
public class ApiController {

    public final Logger logger = org.slf4j.LoggerFactory.getLogger(ApiController.class);

    @Autowired
    WebApplicationContext appContext;

    Map<String, Object> controllerBeans = null;
    Map<String, HandlerMapping> allRequestMappings = null;
    List<ApiInfo> controllerList = new ArrayList<ApiInfo>();
    List<JSONObject> allControllerNames = new ArrayList<JSONObject>();

    @RequestMapping(value = "/page/{pageName}", method = RequestMethod.GET)
    public String page(@PathVariable("pageName") String pageName, HttpServletRequest request) throws Exception {
        try {
            this.list(request);
        } catch (Exception x) {
            x.printStackTrace();
        }
        return "api";
    }

    /***
     * 仅用于测试接口
     * ****/
    @RequestMapping(value = "/testSession")
    public @ResponseBody
    AjaxResult testSession() {
        AjaxResult result = new AjaxResult(0, "success");
        return result;
    }


    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String list(HttpServletRequest request) {

        String fresh = request.getParameter("fresh");
        if (StringUtils.isNotEmpty(fresh)) {
            allRequestMappings = null;
            controllerBeans = null;
            controllerList.clear();
            allControllerNames.clear();
        }
        String ctxPathString = request.getContextPath();
        String filter = request.getParameter("c");
        if (controllerBeans == null) {
            controllerBeans = appContext.getBeansWithAnnotation(org.springframework.stereotype.Controller.class);
        }
        if (controllerList.size() == 0) {
            Set<Entry<String, Object>> sets = controllerBeans.entrySet();
            for (Entry<String, Object> entry : sets) {
                Object obj = entry.getValue();
                // String key = entry.getKey();
                Class clazz = obj.getClass();
                Class entityClass = null;
                Type type = clazz.getGenericSuperclass();
                if (type instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) type;
                    entityClass = (Class) parameterizedType.getActualTypeArguments()[0];
                }

                Annotation pathAnnotion = clazz.getAnnotation(RequestMapping.class);
                if (pathAnnotion != null) {
                    String controller = ((RequestMapping) pathAnnotion).value()[0];
                    if ("/api".equals(controller) || "${server.error.path:${error.path:/error}}".equals(controller)) {
                        continue;
                    }
                    controller = controller.replaceFirst("/", "");
                    JSONObject modelObj = new JSONObject();
                    modelObj.put("name", controller);
                    Annotation modelDesAnnotion = clazz.getAnnotation(ApiDescript.class);
                    String desc = "无说明";
                    if (modelDesAnnotion != null) {
                        desc = ((ApiDescript) modelDesAnnotion).value();
                    }
                    modelObj.put("desc", desc);
                    allControllerNames.add(modelObj);
                    ApiInfo actionList = new ApiInfo(controller, desc);
                    controllerList.add(actionList);
                    Method[] methods = clazz.getDeclaredMethods();
                    List<ActionInfo> actions = getMethodDetails(methods, controller, entityClass, ctxPathString);
                    for (ActionInfo act : actions) {
                        actionList.putAction(act);
                    }
                    // 获取基类上的 方法
                    while (clazz != Object.class) {
                        clazz = clazz.getSuperclass();
                        methods = clazz.getDeclaredMethods();
                        actions = getMethodDetails(methods, controller, entityClass, ctxPathString);
                        for (ActionInfo act : actions) {
                            actionList.putAction(act);
                        }
                    }
                }
            }
        }
        List<ApiInfo> retList;
        if (StringUtils.isNotEmpty(filter)) {
            retList = new ArrayList<ApiInfo>();
            for (ApiInfo p : controllerList) {
                if (p.controller.equals(filter)) {
                    retList.add(p);
                }
            }
        } else {
            retList = controllerList;
        }

        String json = JSONObject.toJSONString(retList, SerializerFeature.DisableCircularReferenceDetect);
        request.setAttribute("apilist", json);

        request.setAttribute("controllers", JSONObject.toJSONString(allControllerNames));
        return "api";

    }

    void loopClazzFeilds(Field[] entityFields,JSONObject paramsObj, JSONObject paramsDesp,String tmpAct){
        for (int i = 0, len = entityFields.length; i < len; ++i) {
            Field filed = entityFields[i];
            String name = filed.getName();
            if(StringUtils.isNotEmpty(tmpAct)){
                if ("serialVersionUID".equals(name) || "updateTime".equals(name)
                        || "createTime".equals(name) || "createUserId".equals(name) || "updateUserId".equals(name)) {
                    continue;
                }
            }

            NotApiParam notApiNon = filed.getAnnotation(NotApiParam.class);
            if (notApiNon != null) {
                continue;
            }
            ApiParams apiDescp = filed.getAnnotation(ApiParams.class);
            String fieldDesc = name;
            if (apiDescp != null) {
                fieldDesc = apiDescp.value();
            }
            if ("add".equals(tmpAct) || "/add".equals(tmpAct)) {
                if (!"id".equals(name) && !"createTime".equals(name)  && !name.contains("createUser")) {
                    paramsObj.put(name, "");
                } else {
                    fieldDesc = "";
                }
            } else if ("update".equals(tmpAct) || "/update".equals(tmpAct)) {
                if ( !"updateTime".equals(name)  && !name.contains("updateUser")) {
                    paramsObj.put(name, "");
                } else {
                    fieldDesc = "";
                }
            } else{
                paramsObj.put(name, "");
            }
            if (!"".equals(fieldDesc)) {
                paramsDesp.put(name, fieldDesc);
            }
        }
    }

    private List<ActionInfo> getMethodDetails(Method[] methods, String controller, Class entityClass,
                                              String ctxPathString) {
        List<ActionInfo> list = new ArrayList<ActionInfo>();
        for (Method f : methods) {
            String methodName = f.getName();
            if ("page".equals(methodName) || "checkresponse".equals(methodName) || "checkFileCreate".equals(methodName)) {
                continue;
            }
            RequestMapping reqMap = f.getAnnotation(RequestMapping.class);
            if (reqMap != null) {
                String[] paths = reqMap.value();
                if (paths.length == 0) {
                    continue;
                }
                String action = paths[0];
                if (StringUtils.isNotEmpty(action)) {
                    ActionInfo resMap = new ActionInfo();

                    String url = controller + "/" + action.replaceFirst("/", "");
                   /* if (!ctxPathString.equals("/")) {
                        url = ctxPathString +"/"+ url;
                    }*/
                    resMap.url = url.replaceAll("//", "/");

                    ApiDescript apiDesc = f.getAnnotation(ApiDescript.class);/**api描述***/
                    ApiParams apiPrs = f.getAnnotation(ApiParams.class);/***api参数**/
                    ApiParamsDesc prsDesc = f.getAnnotation(ApiParamsDesc.class);/*****参数说明*****/
                    ApiReturnDesc returnDesc = f.getAnnotation(ApiReturnDesc.class);/*****返回说明*****/
                    if (returnDesc != null) {
                        resMap.returnDesp = returnDesc.value();
                    } else {
                        resMap.returnDesp = "{code:'0(成功)/1(失败)',message:'信息',data:'数据(对象)',fileds:'其他'}";
                    }
                    String paramString = "";
                    String paramDescString = "";
                    resMap.params = "{}";
                    resMap.paramsDesp = "{}";
                    resMap.descript = "无接口描述";
                    /****参数描述****/
                    if (prsDesc != null) {
                        paramDescString = prsDesc.value();
                    }
                    /****接口描述****/
                    if (apiDesc != null) {
                        resMap.descript = apiDesc.value();
                    }
                    /*****参数*****/
                    if (apiPrs != null && !apiPrs.value().startsWith("vo:")) {
                        paramString = apiPrs.value();
                    }else if(apiPrs != null && apiPrs.value().startsWith("vo:")){
                        String clazz = apiPrs.value().replace("vo:","");
                        Class prsClazz = null;
                        try{
                            prsClazz = Class.forName(clazz);
                        }catch (Exception x){
                            logger.error("",x);
                        }
                        if(prsClazz != null){
                            String tmpAct = action.replaceAll("//", "");
                            Field[] entityFields = prsClazz.getDeclaredFields();
                            JSONObject paramsObj = new JSONObject();
                            JSONObject paramsDesp = new JSONObject();
                            loopClazzFeilds(entityFields,paramsObj,paramsDesp,"");
                            paramString = paramsObj.toString();
                            paramDescString = paramsDesp.toString();
                        }
                    }else if (entityClass != null && ("add".equals(methodName) || "update".equals(methodName))) {
                        String tmpAct = action.replaceAll("//", "");
                        Field[] entityFields = entityClass.getDeclaredFields();
                        JSONObject paramsObj = new JSONObject();
                        JSONObject paramsDesp = new JSONObject();
                        loopClazzFeilds(entityFields,paramsObj,paramsDesp,tmpAct);
                        if (StringUtils.isEmpty(paramDescString)) {
                            paramDescString = paramsDesp.toString();
                        }
                        paramString = paramsObj.toString();
                    }
                    if (!"".equals(paramString)) {
                        resMap.params = paramString;
                    }
                    if (!"".equals(paramDescString)) {
                        resMap.paramsDesp = paramDescString;
                    }
                    list.add(resMap);
                }
            }
        }
        return list;
    }




    class ApiInfo {
        String controller;

        public String getDescription() {
            return description;
        }

        String description;
        List<ActionInfo> actions;

        public ApiInfo(String controller, String description) {
            this.controller = controller;
            this.description = description;
            actions = new ArrayList<ActionInfo>();
        }


        public void putAction(ActionInfo info) {
            actions.add(info);
        }

        public String getController() {
            return controller;
        }

        public List<ActionInfo> getActions() {
            return actions;
        }

    }

    class ActionInfo {
        String descript;
        String url;
        String params;
        String paramsDesp;

        public String getReturnDesp() {
            return returnDesp;
        }

        public void setReturnDesp(String returnDesp) {
            this.returnDesp = returnDesp;
        }

        String returnDesp;


        public String getParamsDesp() {
            return paramsDesp;
        }

        public void setParamsDesp(String paramsDesp) {
            this.paramsDesp = paramsDesp;
        }


        public String getDescript() {
            return descript;
        }

        public void setDescript(String descript) {
            this.descript = descript;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getParams() {
            return params;
        }

        public void setParams(String params) {
            this.params = params;
        }
    }
}
